// Copyright (c) Microsoft Corporation. All rights reserved.

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace Microsoft.TeamFoundation.Migration.Toolkit.Wit
{
    /// <summary>
    /// A pool of work item stores; used to work with the stores from different thread. Every thread
    /// obtains a store from the pool when needed and returns it back when done, making it available
    /// for other threads. If there're no available stores when a store is requested, a new store gets
    /// created and a new connection is established.
    /// </summary>
    class StorePool
    {
        private object m_lock = new object();               // Synchronization object
        private List<IMigrationWorkItemStore> m_all;        // All work item stores
        private Queue<IMigrationWorkItemStore> m_available; // Available work item stores
        private int m_maxUsed;                              // Max number of simultaneously used stores

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="store">Work item store that will be used by the pool</param>
        public StorePool(
            IMigrationWorkItemStore store)
        {
            m_all = new List<IMigrationWorkItemStore>();
            m_available = new Queue<IMigrationWorkItemStore>();

            m_all.Add(store);
            m_available.Enqueue(store);
            m_maxUsed = 1;
        }

        /// <summary>
        /// Gets a work item store from the pool. If no stores are available, it creates a new one.
        /// </summary>
        /// <returns>Work item store object</returns>
        public IMigrationWorkItemStore GetStore()
        {
            IMigrationWorkItemStore store;

            lock (m_lock)
            {
                if (m_available.Count == 0)
                {
                    store = m_all[0].Reopen();
                    m_all.Add(store);
                }
                else
                {
                    store = m_available.Dequeue();
                }
                int used = m_all.Count - m_available.Count;
                if (used > m_maxUsed)
                {
                    m_maxUsed = used;
                }
            }
            return store;
        }

        /// <summary>
        /// Returns store that is no longer used.
        /// </summary>
        /// <param name="store">Work item store</param>
        public void ReturnStore(
            IMigrationWorkItemStore store)
        {
            lock (m_lock)
            {
                m_available.Enqueue(store);
            }
        }

        /// <summary>
        /// The method refreshes all active connections and closes those that were not used
        /// during the last pass.
        /// </summary>
        public void Refresh()
        {
            lock (m_lock)
            {
                Debug.Assert(m_all.Count == m_available.Count, "Some stores are still in use!");
                if (m_maxUsed < m_all.Count)
                {
                    // Close all stores that will no longer be used
                    for (int i = m_maxUsed; i < m_all.Count; i++)
                    {
                        m_all[i].Close();
                    }

                    // Remove all stores that were not used since the last pass.
                    m_all.RemoveRange(m_maxUsed, m_all.Count - m_maxUsed);
                    m_available = new Queue<IMigrationWorkItemStore>(m_all);
                }

                // Refresh all remaining stores
                for (int i = 1; i < m_all.Count; i++)
                {
                    m_all[i].Refresh();
                }
            }
        }

        /// <summary>
        /// Closes the pool.
        /// </summary>
        public void Close()
        {
            lock (m_lock)
            {
                Debug.Assert(m_all.Count == m_available.Count, "Some stores are still in use!");

                // First store in the pool must not be closed - it is shared.
                for (int i = 1; i < m_all.Count; i++)
                {
                    m_all[i].Close();
                }
            }
        }
    }
}
